<!--
 * @Author: c
 * @Date: 2024-02-27 01:27:44
 * @LastEditTime: 2024-03-01 00:07:05
 * @LastEditors: c
 * @Description: 
 * @FilePath: \music-web-admin\src\views\application\music\video\index.vue
 * https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<script setup lang="ts">
defineOptions({
  // eslint-disable-next-line vue/no-reserved-component-names
  name: "Video",
  inheritAttrs: false,
});

import { listArtistOptions } from "@/api/application/artist";
import { getCateAllOptions } from "@/api/application/cate";
import {
  addVideo,
  deleteVideo,
  getVideoForm,
  listVideos,
  updateVideo,
} from "@/api/application/video";
import {
  RateFileInfo,
  VideoForm,
  VideoQuery,
  VideoVO,
} from "@/api/application/video/types";
import { getPresignedUrlByName, pureUpload } from "@/api/minio";
// import { UploadFileDto } from "@/api/minio/type";
import SvgIcon from "@/components/SvgIcon/index.vue";
import { fileToBase64, getVideoPosterInfo } from "@/utils/ffmpeg";

import { listSongOptions } from "@/api/application/song";
import { UploadFileDto, UploadVideoForm } from "@/api/minio/type";
import { video_mime_type } from "@/const";
import { LangEnum } from "@/enums";
import { useAudioStore } from "@/store/modules/audio";
import { checkMediaFile } from "@/utils";
import { Delete } from "@element-plus/icons-vue";
import { FFmpeg } from "@ffmpeg/ffmpeg";
import {
  ElLoading,
  UploadFile,
  UploadInstance,
  UploadProps,
} from "element-plus";
import { OptionType } from "element-plus/es/components/select-v2/src/select.types";
import { storeToRefs } from "pinia";
import { onMounted, reactive, ref } from "vue";
const store = useAudioStore();
const { currentTime, currentTimeStr, lrcData, lrcText } = storeToRefs(store);
let ffmpeg = ref<FFmpeg>();
let audio = ref<HTMLAudioElement>();
let lrcType = ref<boolean>(true);

const queryFormRef = ref(ElForm);
const videoFormRef = ref(ElForm);
const queryParams = reactive<VideoQuery>({
  pageNum: 1,
  pageSize: 10,
});
const loading = ref(false);
const filterLoading = ref(false);
const dialog = reactive({
  title: "",
  visible: false,
});

const videoOptions = reactive({
  // width: '800px', //播放器高度
  // height: '450px', //播放器高度
  // color: "#409eff", //主题色
  // title: '', //视频名称
  // src: "https://go.dreamwq.com/videos/IronMan.mp4", //视频源
  // muted: false, //静音
  // webFullScreen: false,
  // speedRate: ["0.75", "1.0", "1.25", "1.5", "2.0"], //播放倍速
  // autoPlay: false, //自动播放
  // loop: false, //循环播放
  // mirror: false, //镜像画面
  // ligthOff: false,  //关灯模式
  // volume: 0.3, //默认音量大小
  // control: true, //是否显示控制器
});

const uploadDialog = reactive({
  title: "",
  visible: false,
  active: 1,
});
const lrcDialog = reactive({
  visible: false,
  title: "填写歌词",
});
const lrcFormData = reactive({
  audioUrl: "",
  album: "",
  title: "",
  editor: "",
  artist: "",
});

watch(
  () => lrcData.value.title,
  (nv, ov) => {
    if (nv) {
      formData.title = nv;
    }
  }
);

const validateVideo = (rule: any, value: any, callback: any) => {
  console.log(rule, value);
  if (!tmpVideo) {
    callback(new Error("请上传视频"));
  } else {
    if (!tmpVideo?.id && tmpVideo?.raw?.name) {
      // const { url, objectName } =  getPresignedUrlByName(tmpVideo!.raw!.name, false)
      //获取上传链接
      getPresignedUrlByName(tmpVideo!.raw!.name, false)
        .then(async ({ url, objectName }) => {
          //上传
          let loading = null;
          try {
            const { imageData } = await getVideoPosterInfo(tmpVideo!.url!);
            // const { imageData } = await getVideoPosterInfo("http://127.0.0.1:9000/public/%E3%80%8EMV%E3%80%8F%E6%AF%9B%E4%B8%8D%E6%98%93Mao%20Buyi%20-%20%E5%80%9F%20%E5%AE%98%E6%96%B9%E9%AB%98%E7%95%AB%E8%B3%AA%20Official%20HD%20MV-%281080p%29.mp4")
            const imageName =
              tmpVideo!.raw!.name.slice(
                0,
                tmpVideo!.raw!.name.lastIndexOf(".")
              ) + ".png";

            const image = await getPresignedUrlByName(imageName);
            await pureUpload(
              image.url,
              new File([imageData!], imageName, { type: imageData!.type }),
              (loaded, total) => {
                console.log("上传进度", loaded, total);
              }
            );
            await pureUpload(url, tmpVideo.raw!, (loaded, total) => {
              console.log("上传进度", loaded, total);
              loading = ElLoading.service({
                lock: true,
                text: `${((loaded / total) * 100).toFixed(0)}%`,
                background: "rgba(0, 0, 0, 0.7)",
              });
            });
            formData.objectName = tmpVideo.objectName = objectName;
            formData.url = tmpVideo.url = "";
            formData.poster = tmpVideo.poster = image.url.split("?").at(0);
            // formData.video = tmpVideo!;
            callback();
            loading!.close();
          } catch (error: any) {
            callback(new Error(error));
            loading!.close();
          }

          // uploadVideo(tmpVideo.raw!)
          //   .then(({ id, url }) => {
          //     tmpVideo.id = id;
          //     tmpVideo.url = "";
          //     formData.video = tmpVideo!;
          //     callback();
          //   })
          //   .catch((err) => {
          //     callback(new Error(err));
          //   });
        })
        .catch((err) => {
          callback(new Error(err));
        });
    } else {
      callback();
    }
  }
};

const upload = ref<UploadInstance>();
const previewVideo = ref<HTMLVideoElement>();
// const playVideo = ref<TrackLinkVO>();
const uploadVideoRef = ref<UploadInstance>();
const videoList = ref<VideoVO[]>([]);
const artistOptions = ref<OptionType[]>([]);
const songOptions = ref<OptionType[]>([]);
const albumOptions = ref<OptionType[]>([]);
const cateOptions = ref<OptionType[]>([]);
const uploadUrl = ref<string>();

const formData = reactive<VideoForm>({
  sort: 0,
  title: "",
  genre: undefined,
  isActive: true,
  artistIds: [],
  introduce: "",
  downTime: new Date(),
  pushTime: new Date(),
  lang: LangEnum.CN,
  lyric: "",
  isVip: false,
  pic: [],
  indexDataAddTime: new Date(),
  originalReleaseDate: new Date(),
  rateList: [],
  rateFileInfos: [],
  albumIds: [],
  poster: "",
});

const tmpRateFileInfos = ref<RateFileInfo[]>([]);
const tmpPic = ref<Array<UploadFileDto>>([]);
const tmpVideo = reactive<UploadVideoForm>({
  id: "",
  url: "",
  name: "",
  status: "ready",
  uid: 0,
  poster: "",
  size: 0,
  lastModified: 0,
  type: "",
  webkitRelativePath: "",
});
const removeIds = ref([]); // 删除视频ID集合 用于批量删除
// const rules = reactive({
//   video: [{ required: true, validator: validateVideo, trigger: "submit" }],
//   title: [{ required: true, message: "请选择歌单", trigger: "blur" }],
//   artistIds: [{ required: !formData.songIds?.length, message: "请选择歌手", trigger: "blur" }],
//   indexDataAddTime: [{ required: true, message: "请选择上架时间", trigger: "blur" }],
//   originalReleaseDate: [{ required: true, message: "请选择发行时间", trigger: "blur" }],
//   videoName: [{ required: true, message: "请输入视频名称", trigger: "blur" }],
// });
const rules = computed(() => {
  return {
    video: [{ required: true, validator: validateVideo, trigger: "submit" }],
    title: [
      {
        required: !formData.songIds?.length,
        message: "请选择歌单",
        trigger: "blur",
      },
    ],
    genre: [
      {
        required: !formData.songIds?.length,
        message: "请选择类别",
        trigger: "blur",
      },
    ],
    lang: [
      {
        required: !formData.songIds?.length,
        message: "请选择语言",
        trigger: "blur",
      },
    ],
    introduce: [
      {
        required: !formData.songIds?.length,
        message: "请填写介绍",
        trigger: "blur",
      },
    ],
    artistIds: [
      {
        required: !formData.songIds?.length,
        message: "请选择歌手",
        trigger: "blur",
      },
    ],
    indexDataAddTime: [
      { required: true, message: "请选择上架时间", trigger: "blur" },
    ],
    originalReleaseDate: [
      { required: true, message: "请选择发行时间", trigger: "blur" },
    ],
    videoName: [{ required: true, message: "请输入视频名称", trigger: "blur" }],
  };
});
const total = ref(0); // 数据总数
// 选择表格的行视频ID
const selectedRowVideoId = ref<number | undefined>();

const videoMimeType = computed(() => Object.keys(video_mime_type).join(","));

watch(
  () => formData.songIds,
  (newValue) => {
    if (newValue?.length) {
      videoFormRef.value.clearValidate("title");
      videoFormRef.value.clearValidate("video");
    }
  }
);

/**
 * 查询
 */
async function handleQuery() {
  // 重置父组件
  loading.value = true;
  try {
    const data = await listVideos(queryParams);

    // videoList.value = data;

    videoList.value = data.list;
    total.value = data.total;
  } catch (error) {
  } finally {
    loading.value = false;
  }
}

/** 重置查询 */
function resetQuery() {
  queryFormRef.value.resetFields();
  handleQuery();
}

/**行点击事件 */
function onRowClick(row: VideoVO) {
  selectedRowVideoId.value = row.id;
}

/** 行选中 */
function handleSelectionChange(selection: any) {
  removeIds.value = selection.map((item: any) => item.id);
}

/**
 * 播放视频
 */
function openPlayDialog(id: string) {}

/**
 * 打开表单弹窗
 *
 * @param parentId 父视频ID
 * @param videoId 视频ID
 */
async function openDialog(parentId?: number, videoId?: number) {
  cateOptions.value = await getCateAllOptions(2);
  // cateOptions.value = data

  dialog.visible = true;
  if (videoId) {
    dialog.title = "编辑视频";
    const data = await getVideoForm(videoId);
    Object.assign(formData, data);

    formData.artistIds = data.artistList?.map(({ id }) => id) ?? [];
    formData.songIds = data.songIds;
    artistOptions.value =
      data?.artistList?.map<any>((artist) => ({
        label: artist.name,
        value: artist.id,
      })) ?? [];
    songOptions.value = data.songOptions!;
    tmpVideo.id = data.id!;
    tmpVideo.url = data?.url;
    tmpVideo.poster = data?.poster ?? "";
    console.log(formData, data);
  } else {
    dialog.title = "新增视频";
  }
}

/** 视频保存提交 */
function submitForm() {
  videoFormRef.value.validate(async (isValid: boolean) => {
    if (isValid) {
      try {
        if (formData.id) {
          await updateVideo(formData.id, formData);
        } else {
          await addVideo(formData);
        }

        ElMessage.success(`${formData.id ? "修改" : "新增"}成功`);

        closeDialog();
        handleQuery();
      } catch (error) {
        throw error;
      } finally {
      }
    }
  });
}

/** 删除视频 */
function handleDelete(id?: string) {
  const ids = id ?? removeIds.value;
  if (!ids) {
    ElMessage.warning("请勾选删除项");
    return false;
  }

  ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(async () => {
      await deleteVideo(ids);
      ElMessage.success("删除成功");
      handleQuery();
    })
    .catch(() => ElMessage.info("已取消删除"));
}

/**
 * 筛选歌手名称
 * @param query
 */
const filterArtist = async (query: string) => {
  if (query !== "") {
    filterLoading.value = true;
    artistOptions.value = await listArtistOptions({
      keywords: query,
      pageNum: 1,
      pageSize: 10,
    });
    filterLoading.value = false;
  } else {
    artistOptions.value = [];
  }
};

/**
 * 筛选歌曲名称
 * @param query
 */
const filterSong = async (query: string) => {
  if (query !== "") {
    filterLoading.value = true;
    songOptions.value = await listSongOptions({
      songName: query,
      pageNum: 1,
      pageSize: 10,
    });
    filterLoading.value = false;
  } else {
    // songOptions.value = [];
  }
};

/**
 * 播放视频
 */
function playVideo() {}

/** 关闭弹窗 */
function closeDialog() {
  dialog.visible = false;
  resetForm();
}

/** 重置表单 */
function resetForm() {
  videoFormRef.value.resetFields();
  videoFormRef.value.clearValidate();
  tmpVideo.id = "";
  tmpVideo.url = "";
  formData.id = undefined;
  // formData.songIds = []
  formData.url = "";
  formData.sort = 1;
}

/**
 * 选择视频
 * @param uploadFile
 */
const videoFileChange: UploadProps["onChange"] = async (
  uploadFile: UploadFile
) => {
  // new Blob([uploadFile!.raw!.buffer], { type: 'audio/mpeg' })
  const result = await checkMediaFile(uploadFile!.raw! as File);
  console.log(result);

  const [general, video, audio] = result.media.track;

  const { Duration, Width, Height } = video;
  const { url, filename } = await fileToBase64(
    uploadFile!.raw!,
    ({ loaded, total }: ProgressEvent) => {
      console.log(((loaded / total) * 100).toFixed(2));
    }
  );

  let videoDto: UploadFileDto = uploadFile as UploadFileDto;
  // form.id = id;
  // form.url = URL.createObjectURL(uploadFile!.raw!);
  tmpVideo.raw = uploadFile!.raw!;
  tmpVideo.url = url;
  formData.width = tmpVideo.width = Width;
  formData.height = tmpVideo.height = Height;
  formData.duration = tmpVideo.duration = Duration;
};

/**
 * 清空视频
 */
const clearVideoTmp = () => {
  tmpVideo.poster = "";
  tmpVideo.url = "";
  tmpVideo.id = "";
};

onMounted(() => {
  handleQuery();
  // MediaInfoFactory().then(() => {
  //   console.log('Loaded');
  // });
});
</script>

<template>
  <div class="app-container">
    <div class="search-container">
      <el-form ref="queryFormRef" :model="queryParams" :inline="true">
        <el-form-item label="关键字" prop="title">
          <el-input
            v-model="queryParams.videoName"
            placeholder="视频名称"
            clearable
            @keyup.enter="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery"
            ><template #icon><i-ep-search /></template>搜索</el-button
          >
          <el-button @click="resetQuery">
            <template #icon><i-ep-refresh /></template>
            重置</el-button
          >
        </el-form-item>
      </el-form>
    </div>

    <el-card shadow="never" class="table-container">
      <template #header>
        <el-button
          v-hasPerm="['sys:video:create']"
          type="success"
          @click="openDialog(0)"
        >
          <template #icon><i-ep-plus /></template>
          新增</el-button
        >
        <el-button
          v-hasPerm="['sys:video:delete']"
          type="danger"
          :disabled="removeIds.length === 0"
          @click="handleDelete()"
          ><i-ep-delete />删除</el-button
        >
      </template>

      <el-table
        v-loading="loading"
        :data="videoList"
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="50" />
        <!-- <el-table-column label="id" align="left" width="50" prop="id" /> -->

        <el-table-column label="视频名称" min-width="150">
          <template #default="scope">
            <svg-icon :icon-class="scope.row.icon" />
            {{ scope.row.title }}
          </template>
        </el-table-column>
        <el-table-column label="标签" min-width="150">
          <template #default="scope">
            <el-tag type="info">
              {{ scope.row.genre?.at(0)?.categoryName ?? "" }}</el-tag
            >
          </template>
        </el-table-column>
        <el-table-column label="歌手" min-width="150">
          <template #default="scope">
            {{
              scope.row.artistList.map((artist: any) => artist.name).join(",")
            }}
          </template>
        </el-table-column>
        <el-table-column label="语言" align="left" width="150" prop="lang" />

        <el-table-column label="状态" align="center" width="200">
          <template #default="scope">
            <el-tag v-if="scope.row.isActive" type="success">正常</el-tag>
            <el-tag v-else type="info">禁用</el-tag>
          </template>
        </el-table-column>

        <!-- <el-table-column label="排序" align="center" width="300" prop="sort" /> -->
        <el-table-column
          label="创建时间"
          align="left"
          width="200"
          prop="indexDataAddTime"
        />
        <el-table-column
          label="发行时间"
          align="left"
          width="200"
          prop="originalReleaseDate"
        />
        <el-table-column fixed="right" align="center" label="操作" width="220">
          <template #default="scope">
            <el-button
              type="primary"
              link
              size="small"
              @click.stop="openPlayDialog(scope.row.id)"
            >
              <i-ep-plus />播放
            </el-button>

            <el-button
              v-hasPerm="['sys:video:update']"
              type="primary"
              link
              size="small"
              @click.stop="openDialog(undefined, scope.row.id)"
            >
              <i-ep-edit />编辑
            </el-button>
            <el-button
              v-hasPerm="['sys:video:delete']"
              type="primary"
              link
              size="small"
              @click.stop="handleDelete(scope.row.id)"
              ><i-ep-delete />
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>

      <template #footer>
        <pagination
          v-if="total > 0"
          v-model:total="total"
          v-model:page="queryParams.pageNum"
          v-model:limit="queryParams.pageSize"
          @pagination="handleQuery"
        />
      </template>
    </el-card>

    <!-- 添加视频 -->
    <el-dialog
      v-model="dialog.visible"
      :title="dialog.title"
      destroy-on-close
      append-to-body
      width="800px"
      @close="closeDialog"
      :draggable="true"
      top="5vh"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
    >
      <el-form
        ref="videoFormRef"
        :model="formData"
        :rules="rules"
        label-width="100px"
      >
        <el-form-item label="关联歌曲" prop="songIds">
          <el-select-v2
            v-model="formData.songIds"
            :multiple-limit="1"
            style="width: 240px"
            multiple
            filterable
            remote
            :remote-method="filterSong"
            clearable
            :options="songOptions"
            :loading="filterLoading"
            placeholder="选择歌曲"
          />
        </el-form-item>

        <el-form-item label="关联歌手" prop="artistIds">
          <el-select-v2
            :disabled="!!formData.songIds?.length"
            v-model="formData.artistIds"
            style="width: 240px"
            multiple
            filterable
            remote
            :remote-method="filterArtist"
            clearable
            :options="artistOptions"
            :loading="filterLoading"
            placeholder="选择歌手"
          />
        </el-form-item>

        <el-form-item label="视频名称" prop="title">
          <el-input
            :disabled="!!formData.songIds?.length"
            v-model="formData.title"
            placeholder="请输入歌曲名称"
          />
        </el-form-item>

        <el-form-item label="视频" prop="video">
          <el-upload
            v-if="!(tmpVideo && tmpVideo.url)"
            ref="uploadVideoRef"
            class="upload-video"
            :accept="videoMimeType"
            :auto-upload="false"
            :on-change="videoFileChange"
            :show-file-list="false"
          >
            <template #trigger>
              <el-button class="upload-btn">上传视频</el-button>
            </template>
          </el-upload>
          <div class="upload-video" v-else>
            <el-icon
              class="avatar-icon avatar-delete-icon delete-video-icon"
              @click="clearVideoTmp"
            >
              <Delete />
            </el-icon>
            <!-- <vue3-video-player :poster="tmpVideo!.poster!" :crossorigin="'anonymous'" class="upload-video"
              @play="playVideo" :src="tmpVideo!.url!"></vue3-video-player> -->
            <video
              ref="previewVideo"
              controls
              crossorigin="anonymous"
              class="upload-video"
              @play="playVideo"
            >
              <source :src="tmpVideo!.url!" type="video/mp4" />
            </video>
          </div>
          <!-- <video ref="previewVideo" v-else controls crossorigin="anonymous" class="upload-video" @play="playVideo">
            <source :src="tmpVideo!.url!" type="video/mp4" />
          </video> -->
        </el-form-item>

        <el-form-item label="标签" prop="genre">
          <el-select-v2
            :disabled="!!formData.songIds?.length"
            v-model="formData.genre"
            filterable
            :options="cateOptions"
            placeholder="选择标签"
            style="width: 240px"
            :multiple="false"
          />
        </el-form-item>
        <el-form-item label="语言" prop="lang">
          <dictionary
            :disabled="!!formData.songIds?.length"
            v-model="formData.lang"
            type-code="lang"
          />
        </el-form-item>
        <el-form-item label="介绍" prop="introduce">
          <el-input
            :disabled="!!formData.songIds?.length"
            type="textarea"
            v-model="formData.introduce"
            placeholder="请输入歌手介绍"
            rows="6"
            autosize
          />
        </el-form-item>
        <el-form-item label="发行日期" prop="pushTime">
          <el-date-picker
            v-model="formData.originalReleaseDate"
            type="date"
            placeholder="选择发行日期"
          />
        </el-form-item>
        <el-form-item label="上架时间" prop="pushTime">
          <el-date-picker
            v-model="formData.indexDataAddTime"
            type="datetime"
            placeholder="选择上架时间"
          />
        </el-form-item>

        <el-form-item label="状态" prop="isActive">
          <el-switch v-model="formData.isActive" />
        </el-form-item>
        <el-form-item label="排序" prop="sort">
          <el-input-number
            v-model="formData.sort"
            style="width: 100px"
            controls-position="right"
            :min="0"
          />
        </el-form-item>
      </el-form>

      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确 定</el-button>
          <el-button @click="closeDialog">取 消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<style scoped lang="scss">
.avatar {
  display: block;
  width: 178px;
  height: 178px;
  object-fit: scale-down;
}

.avatar-cover-wrapper {
  position: absolute;
  top: 0;
  left: 0;
  // cursor: default;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  // pointer-events: none;
  width: 100%;
  height: 100%;
  font-size: 20px;
  color: #fff;
  cursor: pointer;
  background-color: var(--el-overlay-color-lighter);
  opacity: 0;
  transition: opacity var(--el-transition-duration);

  .avatar-icon {
    font-size: 20px;
  }

  .avatar-zoomin-icon {
    margin-right: 15px;
  }
}

.avatar-delete-wrapper {
  position: relative;
  border: 1px dashed gray;

  &:hover {
    .avatar-cover-wrapper {
      opacity: 1;
    }
  }
}

.upload-video {
  // background-color: #000;
  position: relative;
  width: 500px;
  // height: 150px;
  aspect-ratio: 16 / 9;

  .upload-btn {
    width: 500px;
    height: 281.25px;
    aspect-ratio: 16 / 9;
  }
}

.delete-video-icon {
  position: absolute;
  top: 20px;
  right: 20px;
  z-index: 1000;
  color: #fff;
}

.el-upload {
  position: relative;
  overflow: hidden;
  cursor: pointer;
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  transition: var(--el-transition-duration-fast);
}
</style>

<style lang="scss">
.play-progress {
  margin-bottom: 15px;
}

.avatar-uploader .el-upload {
  position: relative;
  overflow: hidden;
  cursor: pointer;
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
  border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
  width: 178px;
  height: 178px;
  font-size: 28px;
  color: #8c939d;
  text-align: center;
}
</style>
